import itertools
import math
from parsers.constant import *
from deciplex import Deciplex
import os
from decimal import Decimal
import binascii

class QRegister:

   def __init__(self):
      self.__states = {() : Deciplex(1)}

   def __setitem__(self,state,value):
      if value == 0:
         del self.__states[state]
      else:
         self.__states[state] = value

   def __len__(self):
      if self.__states == {}:
         return 0
      else:
         for state in self:
            return len(state)

   def __toKet(self, state, size = 0):
      result = []
      if isinstance(state, int):
         result.append(state)
      else:
         result.extend(state)
      return '|' + '>|'.join([str(s) for s in result]) + '>'

   def __getitem__(self, key):
      return self.__states.get(key, Deciplex())

   def __iter__(self):
      for state in sorted(self.__states.keys()):
         yield state

   def __str__(self):
      result = ''
      for state in self:
         result += str(self[state])
         result += ' ' + self.__toKet(state) + '\t' 
         result += '# ' + str(state)
         result += '\n'
      return result[:-1]

   def __clear(self):
      self.__states = {}

   def check(self, number):
      r = self.__getRandomDecimal()
      for state in self:
         r -= abs(self[state]) ** 2
         if r <= 0:
            result = state[number]
            break
      norm = Decimal(0)
      for state in self:
         if state[number] != result:
            del self[state]
         else:
            norm += self[state].sqr_abs()
      norm = norm.sqrt()
      for state in self:
         self[state] = self[state] / norm
      return result
         
   def __getRandomDecimal(self):
      r = Decimal(2)
      while r > 1:
         randomHex = binascii.hexlify(os.urandom(RANDOM_BYTES))
         r = Decimal(int(randomHex, 16)) / DECIMAL_PRECISION
      return r

   def iteritems(self):
      for state in self:
         yield state, self[state]
   
   def add(self, new_register = 0):
      for state in self:
         self[state + (new_register,)] = self[state]
         del self[state]
      return len(self) - 1
         
   def __delitem__(self, state):
      del self.__states[state]

   def apply_function(self, function, arguments):
      for state in self:
         value = function(*[state[arg] for arg in arguments])
         for coordinate, value_state in value:
            new_state =  state + (value_state,)
            self[new_state] = self[state] * coordinate
         del self[state]
      return len(self) - 1
   